#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
妙码代码生成工具 - Python实现
支持配置文件驱动的代码生成
"""

import sys
import os
import argparse
import subprocess
import re
from pathlib import Path
from datetime import datetime
from typing import Optional, Dict, Any

# 导入文件覆盖策略模块
from file_overwrite_policy import should_overwrite_file


class Logger:
    """日志工具类"""

    # 颜色定义
    RED = '\033[0;31m'
    GREEN = '\033[0;32m'
    YELLOW = '\033[1;33m'
    BLUE = '\033[0;34m'
    PURPLE = '\033[0;35m'
    CYAN = '\033[0;36m'
    NC = '\033[0m'  # No Color

    # 日志级别
    DEBUG = 0
    INFO = 1
    WARN = 2
    ERROR = 3

    def __init__(self, level: int = INFO, show_timestamp: bool = False, log_file: Optional[Path] = None):
        self.level = level
        self.show_timestamp = show_timestamp
        self.log_file = log_file

    def _format_message(self, level_name: str, message: str) -> str:
        """格式化日志消息"""
        timestamp = f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] " if self.show_timestamp else ""
        return f"{timestamp}{level_name} {message}"

    def _write_to_file(self, message: str):
        """将消息写入日志文件"""
        if self.log_file:
            try:
                with open(self.log_file, 'a', encoding='utf-8') as f:
                    # 去除颜色代码后写入文件
                    clean_message = re.sub(r'\033\[[0-9;]*m', '', message)
                    f.write(clean_message + '\n')
            except Exception:
                pass  # 忽略日志文件写入错误

    def debug(self, message: str):
        if self.level <= self.DEBUG:
            formatted = self._format_message(f"{self.CYAN}[DEBUG]{self.NC}", message)
            print(formatted)
            self._write_to_file(formatted)

    def info(self, message: str):
        if self.level <= self.INFO:
            formatted = self._format_message(f"{self.BLUE}[INFO]{self.NC}", message)
            print(formatted)
            self._write_to_file(formatted)

    def warn(self, message: str):
        if self.level <= self.WARN:
            formatted = self._format_message(f"{self.YELLOW}[WARN]{self.NC}", message)
            print(formatted)
            self._write_to_file(formatted)

    def error(self, message: str):
        if self.level <= self.ERROR:
            formatted = self._format_message(f"{self.RED}[ERROR]{self.NC}", message)
            print(formatted, file=sys.stderr)
            self._write_to_file(formatted)

    def success(self, message: str):
        if self.level <= self.INFO:
            formatted = self._format_message(f"{self.GREEN}[SUCCESS]{self.NC}", message)
            print(formatted)
            self._write_to_file(formatted)

    def title(self, title: str):
        formatted = f"{self.PURPLE}=== {title} ==={self.NC}"
        print(formatted)
        self._write_to_file(formatted)


class MiaomaGenerator:
    """妙码代码生成器"""

    def __init__(self):
        # 初始化日志目录和文件
        self.artifacts_dir = Path(__file__).parent / 'artifacts'
        self.artifacts_dir.mkdir(exist_ok=True)

        # 生成时间戳文件夹
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S_%f')[:-3]  # 精确到毫秒
        self.log_session_dir = self.artifacts_dir / timestamp
        self.log_session_dir.mkdir(exist_ok=True)

        # 创建分离的日志文件
        self.gen_log_file = self.log_session_dir / 'gen.log'
        self.export_log_file = self.log_session_dir / 'export.log'
        self.miaoma_log_file = self.log_session_dir / 'miaoma.log'

        # 初始化logger，主要用于gen.log
        self.logger = Logger(log_file=self.gen_log_file)

        self.miaoma_root: Optional[Path] = None
        self.output_dir: Optional[Path] = None
        self.config: Dict[str, Any] = {}
        self.generated_dir: Optional[Path] = None

    def log_export(self, message: str, level: str = "INFO"):
        """记录导出相关的日志"""
        formatted_message = f"[{datetime.now().strftime('%H:%M:%S')}] [{level}] {message}"
        print(formatted_message)
        try:
            with open(self.export_log_file, 'a', encoding='utf-8') as f:
                f.write(formatted_message + '\n')
        except Exception:
            pass

    def log_miaoma(self, message: str):
        """记录miaoma工具的输出日志"""
        try:
            with open(self.miaoma_log_file, 'a', encoding='utf-8') as f:
                f.write(message + '\n')
        except Exception:
            pass

    def show_help(self, prog_name: str):
        """显示帮助信息"""
        help_text = f"""用法: {prog_name} [选项]
选项:
  -o, --output DIR    指定输出目录（可选，如果指定则自动导出生成的代码）
  -h, --help          显示此帮助信息

工具模式（在suantai-project.ini中配置）:
  TOOL_MODE=1         使用gen-code命令（单文件模式）
  TOOL_MODE=2         使用gen-project命令（工程模式）

输出目录优先级:
  1. 命令行参数 -o/--output（优先级最高）
  2. suantai-project.ini中的DEFAULT_OUTPUT_DIR配置项
  3. 如果都未设置，则仅生成代码不导出

示例:
  {prog_name}                                       # 使用默认输出目录或仅生成代码
  {prog_name} -o /path/to/output                    # 生成代码并导出到指定目录（覆盖默认值）
  {prog_name} --output ~/my-project                 # 生成代码并导出到指定目录（覆盖默认值）

日志保存:
  每次执行的日志将分类保存到: tools/artifacts/{datetime.now().strftime('%Y%m%d_%H%M%S_xxx')}/
    - gen.log: 生成过程日志
    - export.log: 导出过程日志
    - miaoma.log: miaoma工具详细输出"""
        print(help_text)

    def parse_arguments(self) -> argparse.Namespace:
        """解析命令行参数"""
        parser = argparse.ArgumentParser(add_help=False)
        parser.add_argument('-o', '--output', help='指定输出目录（可选，如果指定则自动导出生成的代码）')
        parser.add_argument('-h', '--help', action='store_true', help='显示此帮助信息')

        args = parser.parse_args()

        if args.help:
            self.show_help(sys.argv[0])
            sys.exit(0)

        return args

    def ensure_working_directory(self):
        """确定并切换到miaoma根目录"""
        current_dir = Path.cwd()
        script_dir = Path(__file__).parent.absolute()

        self.logger.info(f"当前工作目录: {current_dir}")
        self.logger.info(f"脚本所在目录: {script_dir}")

        # 从脚本目录向上查找miaoma根目录
        search_dir = script_dir
        while search_dir != search_dir.parent:
            if (search_dir / 'miaoma.py').exists() and \
               (search_dir / 'src').is_dir() and \
               (search_dir / 'assets').is_dir():
                self.miaoma_root = search_dir
                break
            search_dir = search_dir.parent

        if not self.miaoma_root:
            self.logger.error("无法找到 miaoma 项目根目录！")
            self.logger.error("请确保脚本在 miaoma 项目目录结构中")
            sys.exit(1)

        self.logger.info(f"找到 miaoma 根目录: {self.miaoma_root}")

        # 切换到根目录
        if current_dir != self.miaoma_root:
            self.logger.info(f"切换工作目录到: {self.miaoma_root}")
            os.chdir(self.miaoma_root)

        self.logger.info("工作目录设置完成")

    def read_config(self):
        """读取配置文件"""
        config_file = self.miaoma_root / 'tools' / 'suantai-project.ini'

        if not config_file.exists():
            self.logger.error(f"配置文件不存在: {config_file}")
            sys.exit(1)

        self.logger.info(f"读取配置文件: {config_file}")

        # 读取配置项
        with open(config_file, 'r', encoding='utf-8') as f:
            for line in f:
                line = line.strip()
                # 跳过注释和空行
                if line.startswith('#') or not line:
                    continue

                if '=' in line:
                    key, value = line.split('=', 1)
                    key = key.strip()
                    value = value.strip()

                    if key in ['MIAOMA_CONFIG_PROJECT_PATH', 'MIAOMA_DATABASE_FILE',
                              'MIAOMA_DATABASE_PATH', 'TOOL_MODE', 'SHOW_PYTHON_LOGS',
                              'DEFAULT_OUTPUT_DIR']:
                        self.config[key] = value

        # 验证必要的配置项
        required_keys = ['MIAOMA_CONFIG_PROJECT_PATH', 'MIAOMA_DATABASE_FILE']
        for key in required_keys:
            if key not in self.config:
                self.logger.error(f"配置文件中缺少必要的配置项: {key}")
                sys.exit(1)

        # 验证TOOL_MODE
        tool_mode = self.config.get('TOOL_MODE', '1')
        if tool_mode not in ['1', '2']:
            self.logger.error(f"无效的TOOL_MODE值: {tool_mode}，必须是1或2")
            sys.exit(1)

        self.logger.info("配置读取完成:")
        self.logger.info(f"  工具模式: {self.config.get('TOOL_MODE', '1')}")
        self.logger.info(f"  项目路径: {self.config['MIAOMA_CONFIG_PROJECT_PATH']}")
        self.logger.info(f"  数据库文件: {self.config['MIAOMA_DATABASE_FILE']}")
        self.logger.info(f"  数据库路径: {self.config.get('MIAOMA_DATABASE_PATH', '')}")
        self.logger.info(f"  默认输出目录: {self.config.get('DEFAULT_OUTPUT_DIR', '未设置')}")
        self.logger.info(f"  显示Python日志: {self.config.get('SHOW_PYTHON_LOGS', 'false')}")

    def check_output_directory(self, output_dir: str):
        """检查输出目录"""
        # 如果输出目录是相对路径，则相对于miaoma根目录
        if not os.path.isabs(output_dir):
            self.output_dir = self.miaoma_root / output_dir
        else:
            self.output_dir = Path(output_dir)

        # 展开用户家目录路径（如 ~/workspace/suantai
        if str(self.output_dir).startswith('~'):
            self.output_dir = Path(str(self.output_dir)).expanduser()

        self.logger.info(f"输出目录: {self.output_dir}")

        # 检查输出目录是否存在，不存在则创建
        if not self.output_dir.exists():
            self.logger.info(f"输出目录不存在，正在创建: {self.output_dir}")
            try:
                self.output_dir.mkdir(parents=True, exist_ok=True)
                self.logger.info("✓ 输出目录创建成功")
            except Exception as e:
                self.logger.error(f"无法创建输出目录: {self.output_dir}")
                self.logger.error(f"错误: {e}")
                sys.exit(1)
        else:
            self.logger.info("✓ 输出目录已存在")

        # 删除输出目录中的旧文件
        artifacts_dir = self.output_dir / 'miaoma' / 'artifacts'
        if artifacts_dir.exists():
            self.logger.info(f"删除输出目录中的旧文件: {artifacts_dir}")
            import shutil
            shutil.rmtree(artifacts_dir)

        self.cleanup_config_directory()

        self.logger.info("✓ 输出目录检查通过")

    def _copy_recursively(self, src: Path, dst: Path):
        """递归拷贝文件和目录，根据模板配置决定是否覆盖文件"""
        import shutil

        if src.is_file():
            # 计算相对于输出目录的路径用于日志显示
            try:
                relative_path = dst.relative_to(self.output_dir)
            except (ValueError, AttributeError):
                # 如果无法计算相对路径，则使用绝对路径
                relative_path = dst

            # 使用文件覆盖策略模块判断是否应该覆盖
            should_overwrite, reason = should_overwrite_file(
                src.name,
                self.config['MIAOMA_CONFIG_PROJECT_PATH'],
                default=True  # 默认覆盖
            )

            # 检查是否应该覆盖文件
            if not should_overwrite and dst.exists():
                self.log_export(f"⏩ 跳过已存在文件: {relative_path} ({reason})")
                # 更新统计
                if hasattr(self, 'file_stats'):
                    self.file_stats['skipped'] += 1
                return

            # 拷贝文件
            if dst.exists():
                self.log_export(f"🔄 覆盖文件: {relative_path} ({reason})")
                # 更新统计
                if hasattr(self, 'file_stats'):
                    self.file_stats['overwritten'] += 1
            else:
                self.log_export(f"📝 创建文件: {relative_path} ({reason})")
                # 更新统计
                if hasattr(self, 'file_stats'):
                    self.file_stats['created'] += 1

            dst.parent.mkdir(parents=True, exist_ok=True)
            shutil.copy2(src, dst)
        elif src.is_dir():
            # 创建目标目录（如果不存在）
            dst.mkdir(parents=True, exist_ok=True)

            # 计算相对路径用于目录日志
            try:
                relative_path = dst.relative_to(self.output_dir)
                self.logger.debug(f"处理目录: {relative_path}")
            except (ValueError, AttributeError):
                self.logger.debug(f"处理目录: {dst}")

            # 递归拷贝目录中的所有内容
            for item in src.iterdir():
                self._copy_recursively(item, dst / item.name)

    def _extract_output_directory(self, log_file: Path):
        """从日志文件中提取输出目录"""
        if not log_file.exists():
            return None

        with open(log_file, 'r', encoding='utf-8') as f:
            for line in f:
                if '[MIAOMA_OUTPUT_DIR]' in line:
                    # 提取输出目录（只取第一个匹配项）
                    match = re.search(r'\[MIAOMA_OUTPUT_DIR\] 输出目录: (.+)', line)
                    if match:
                        extracted_dir = match.group(1).strip()
                        # 构建完整的绝对路径
                        generated_dir = self.miaoma_root / extracted_dir

                        # 保存到文件
                        last_output_file = self.miaoma_root / 'tools' / '.last_output_dir'
                        with open(last_output_file, 'w') as f:
                            f.write(str(generated_dir))

                        self.logger.debug(f"从日志提取的完整输出目录: {generated_dir}")
                        return generated_dir

        return None

    def export_generated_code(self):
        """导出步骤：将生成的代码拷贝到用户指定的输出目录"""
        if not self.output_dir:
            self.logger.info("未指定输出目录，跳过导出步骤")
            self.logger.info(f"生成的代码位于: {self.generated_dir}")
            self.logger.info("提示: 使用 -o 或 --output 选项指定输出目录以启用自动导出功能")
            return

        if not self.generated_dir or not self.generated_dir.exists():
            self.logger.warn("未找到生成的代码目录，跳过导出步骤")
            return

        self.logger.title("步骤 2: 导出生成的代码")
        self.log_export("=== 开始导出生成的代码 ===")
        self.log_export(f"源目录: {self.generated_dir}")
        self.log_export(f"目标目录: {self.output_dir}")

        self.log_export("文件覆盖策略: 根据模板配置文件决定")
        self.log_export("  - Auto*/generated* 前缀文件: 自动覆盖")
        self.log_export("  - manual* 前缀文件: 保护已存在文件")
        self.log_export("  - 其他文件: 按模板配置的 force_overwrite 决定")
        print()  # 空行分隔

        try:
            # 初始化统计计数器
            self.file_stats = {
                'created': 0,
                'overwritten': 0,
                'skipped': 0
            }

            # 遍历生成目录中的所有内容
            for item in self.generated_dir.iterdir():
                target_path = self.output_dir / item.name
                self._copy_recursively(item, target_path)

            print()  # 空行分隔
            self.logger.success("代码导出完成！")
            self.log_export("=== 代码导出完成！ ===", "SUCCESS")
            self.log_export(f"代码已导出到: {self.output_dir}")

            # 显示文件处理统计
            total_files = self.file_stats['created'] + self.file_stats['overwritten'] + self.file_stats['skipped']
            self.log_export(f"文件处理统计:")
            self.log_export(f"  📝 新建文件: {self.file_stats['created']} 个")
            self.log_export(f"  🔄 覆盖文件: {self.file_stats['overwritten']} 个")
            self.log_export(f"  ⏩ 跳过文件: {self.file_stats['skipped']} 个")
            self.log_export(f"  📊 总计处理: {total_files} 个文件")

        except Exception as e:
            self.logger.error(f"代码导出失败: {e}")
            sys.exit(1)

    def copy_sql_files(self):
        """拷贝SQL文件到输出目录"""
        if not self.output_dir:
            return

        self.logger.title("步骤 3: 拷贝SQL文件")
        self.logger.info("开始拷贝SQL文件...")

        # 构建源目录路径
        database_path = self.config.get('MIAOMA_DATABASE_PATH', 'assets/projects/suantai/sql')
        source_sql_dir = self.miaoma_root / database_path

        # 构建目标目录路径
        target_sql_dir = self.output_dir / 'docs' / 'db' / 'tables'

        self.logger.info(f"源SQL目录: {source_sql_dir}")
        self.logger.info(f"目标SQL目录: {target_sql_dir}")

        if not source_sql_dir.exists():
            self.logger.warn(f"源SQL目录不存在: {source_sql_dir}")
            return

        try:
            import shutil

            # 创建目标目录
            target_sql_dir.mkdir(parents=True, exist_ok=True)

            # 拷贝所有SQL文件
            sql_files_copied = 0

            # 获取所有SQL文件并排序
            sql_files = list(source_sql_dir.glob('*.sql'))
            sql_files.sort(key=lambda x: x.name)

            self.logger.info(f"发现SQL文件 ({len(sql_files)} 个): {[f.name for f in sql_files]}")

            for sql_file in sql_files:
                target_file = target_sql_dir / sql_file.name
                shutil.copy2(sql_file, target_file)
                self.logger.info(f"拷贝SQL文件: {sql_file.name}")
                sql_files_copied += 1

            if sql_files_copied > 0:
                self.logger.success(f"SQL文件拷贝完成！共拷贝 {sql_files_copied} 个文件")
                self.logger.info(f"SQL文件已拷贝到: {target_sql_dir}")
            else:
                self.logger.warn("未找到任何SQL文件")

        except Exception as e:
            self.logger.error(f"SQL文件拷贝失败: {e}")

    def cleanup_config_directory(self):
        """清理配置目录"""
        self.logger.title("步骤 4: 清理配置目录")
        self.logger.info("开始清理配置目录...")

        # 构建配置目录路径
        config_dir = Path(self.config['MIAOMA_CONFIG_PROJECT_PATH']) / 'config'

        self.logger.info(f"配置目录: {config_dir}")

        if config_dir.exists() and config_dir.is_dir():
            try:
                import shutil
                shutil.rmtree(config_dir)
                self.logger.success("配置目录清理完成！")
                self.logger.info(f"已删除目录: {config_dir}")
            except Exception as e:
                self.logger.error(f"配置目录清理失败: {e}")
        else:
            self.logger.info("配置目录不存在，无需清理")

    def run_generation(self):
        """执行代码生成"""
        self.logger.info("开始执行代码生成...")

        # 根据TOOL_MODE选择不同的命令
        tool_mode = self.config.get('TOOL_MODE', '1')
        if tool_mode == '1':
            cmd = [
                'python3', 'miaoma.py', 'gen-code',
                '--project-dir', self.config['MIAOMA_CONFIG_PROJECT_PATH'],
                '-f', self.config['MIAOMA_DATABASE_FILE']
            ]
            self.logger.info("使用gen-code命令（单文件模式）")
        elif tool_mode == '2':
            cmd = [
                'python3', 'miaoma.py', 'gen-project',
                '--project-dir', self.config['MIAOMA_CONFIG_PROJECT_PATH'],
                '-d', self.config.get('MIAOMA_DATABASE_PATH', '')
            ]
            self.logger.info("使用gen-project命令（工程模式）")
        else:
            self.logger.error(f"无效的TOOL_MODE值: {tool_mode}")
            sys.exit(1)

        self.logger.info(f"执行命令: {' '.join(cmd)}")

        # 使用会话目录中的miaoma.log文件
        self.logger.info(f"Miaoma输出日志将保存到: {self.miaoma_log_file}")

        try:
            show_logs = self.config.get('SHOW_PYTHON_LOGS', 'false').lower() == 'true'

            if show_logs:
                # 显示日志模式
                self.logger.info("启用详细日志输出模式")
                with open(self.miaoma_log_file, 'w', encoding='utf-8') as log_file:
                    process = subprocess.Popen(
                        cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
                        universal_newlines=True, cwd=self.miaoma_root
                    )

                    for line in process.stdout:
                        print(line, end='')
                        log_file.write(line)
                        # 使用专门的miaoma日志方法
                        self.log_miaoma(line.rstrip())

                    process.wait()
                    if process.returncode != 0:
                        raise subprocess.CalledProcessError(process.returncode, cmd)
            else:
                # 静默模式
                self.logger.info("使用静默模式，详细输出保存到日志文件")
                with open(self.miaoma_log_file, 'w', encoding='utf-8') as log_file:
                    result = subprocess.run(
                        cmd, stdout=log_file, stderr=subprocess.STDOUT,
                        check=True, cwd=self.miaoma_root, text=True
                    )

            # 从日志文件中提取实际生成的输出目录
            self.generated_dir = self._extract_output_directory(self.miaoma_log_file)
            if self.generated_dir:
                self.logger.info(f"检测到生成目录: {self.generated_dir}")
            else:
                self.logger.warn("未能从日志中检测到生成目录")

            self.logger.success("代码生成完成！")
            self.logger.info(f"Miaoma详细输出已保存到: {self.miaoma_log_file}")

        except subprocess.CalledProcessError as e:
            self.logger.error("代码生成失败！")
            self.logger.error(f"返回码: {e.returncode}")
            self.logger.info(f"详细错误信息请查看: {self.miaoma_log_file}")
            if not show_logs:
                self.logger.info("提示: 设置 SHOW_PYTHON_LOGS=true 查看实时详细错误信息")
            sys.exit(1)

    def _write_to_file(self, message: str):
        """辅助方法：写入主日志文件"""
        if self.gen_log_file:
            try:
                with open(self.gen_log_file, 'a', encoding='utf-8') as f:
                    f.write(message + '\n')
            except Exception:
                pass

    def run(self):
        """主执行函数"""
        self.logger.title("妙码代码生成脚本 - 配置版本")
        self.logger.info(f"执行日志将保存到: {self.gen_log_file}")
        print()

        # 解析命令行参数
        args = self.parse_arguments()

        # 确保工作目录正确
        self.ensure_working_directory()

        # 读取配置文件
        self.read_config()

        # 确定输出目录：命令行参数优先，然后是配置文件默认值
        output_dir = None
        if args.output:
            # 命令行参数优先
            output_dir = args.output
            self.logger.info(f"使用命令行指定的输出目录: {output_dir}")
        elif 'DEFAULT_OUTPUT_DIR' in self.config:
            # 使用配置文件中的默认值
            output_dir = self.config['DEFAULT_OUTPUT_DIR']
            # 新增：如果不是绝对路径且不是~开头，则拼接到miaoma根目录的parent下
            if not os.path.isabs(output_dir) and not str(output_dir).startswith('~'):
                output_dir = str(self.miaoma_root.parent / output_dir)
            self.logger.info(f"使用配置文件中的默认输出目录: {output_dir}")

        # 检查输出目录
        if output_dir:
            self.check_output_directory(output_dir)
        else:
            self.logger.info("未指定输出目录，仅生成代码不导出")

        # 执行代码生成
        self.run_generation()

        # 导出生成的代码
        self.export_generated_code()

        # 拷贝SQL文件
        self.copy_sql_files()

        # 清理配置目录
        self.cleanup_config_directory()

        self.logger.success("所有操作完成！")
        self.logger.info(f"日志分类保存到: {self.log_session_dir}")
        self.logger.info(f"  - gen.log: 生成过程日志")
        self.logger.info(f"  - export.log: 导出过程日志")
        self.logger.info(f"  - miaoma.log: miaoma工具详细输出")


if __name__ == '__main__':
    generator = MiaomaGenerator()
    generator.run()